import mongoose from 'mongoose'
import type { Model } from 'mongoose'
import type { Request } from 'express'
import { commLog } from '../utils/log'

/** 根据_id查询资源选项 */
export interface IReqQueryByIdOptions {
  /** 资源ID */
  _id: string,
  /** 需要填充的字段，目前只支持1级填充，多个字段使用 空格 分隔 */
  fill?: string,
}

/** 列表查询请求选项 */
export interface IReqRestOptions {
  query?: IDynamicProp<string | number | [] | Object>, // 查询条件
  field?: string, // 字段, 多个字段使用 空格 分隔
  fill?: string | string[] | Object, // 指定字段填充
  sort?: IDynamicProp<1 | -1>, // 排序字段, 1 升序 -1 降序
  paging?: {
    page: number, // 当前页码
    pagesize: number, // 每页条数
  },
  data?: string | number | Object, // 请求数据，用于单id搜索、创建、更新、删除指定资源
}

export type IAction = ENUM_ACTION.CREATE_MANY | ENUM_ACTION.CREATE_ONE | ENUM_ACTION.DELETE | ENUM_ACTION.LIST | ENUM_ACTION.ONE | ENUM_ACTION.UPDATE
export enum EAction {
  LIST = 'list',
  ID = 'id',
  CREATE = 'create',
  UPDATE = 'update',
  DELETE = 'delete',
}

type TOptions = {
  queries: IDynamicProp<string | [] | Object>,
  filters: Object,
}

/** 操作字符转义为mongodb关键字 */
const operatorConvert = (char: string) => {
  const operators: IDynamicProp<string> = {
    '>': '$gt', 'gt': '$gt',
    '>=': '$gte', 'gte': '$gte',
    '<': '$lt', 'lt': '$lt',
    '<=': '$lte', 'lte': '$lte',
    '<>': '$ne',
    'or': '$or',
    'and': '$and',
    'in': '$in',
    'regex': '$regex',
    'options': '$options'
  }
  return operators[char] || char
}

/** 将对象中的运算符转换为mongodb关键字 */
const convertParamsToOptions = (obj: any): IDynamicProp<any> => {
  const newObj: IDynamicProp<any> = {}
  for (const key in obj) {
    if (Array.isArray(obj[key])) {
      if (typeof obj[key][0] !== 'object')
        newObj[operatorConvert(key)] = obj[key]
      else
        newObj[operatorConvert(key)] = obj[key].map(convertParamsToOptions)
    } else if (typeof obj[key] === 'object') {
      newObj[operatorConvert(key)] = convertParamsToOptions(obj[key])
    } else {
      newObj[operatorConvert(key)] = obj[key]
    }
  }
  return newObj
}

/** 构建列表查询参数 */
const buildOptions = (params: IReqRestOptions): TOptions => {
  const queries: IDynamicProp<string | string[] | Object> = params.query ? convertParamsToOptions(params.query) : {}
  const filters: IDynamicProp<string | string[] | Object> = {}

  if (params.fill) {
    filters['populate'] = params.fill
  }
  if (params.sort) {
    filters['sort'] = params.sort
  }
  if (params.field) {
    filters['select'] = params.field
  }
  if (params.paging) {
    const page = Math.max(params.paging.page ?? 1, 1)
    const pagesize = Math.max(params.paging.pagesize ?? 10, 1)
    filters['skip'] = (page - 1) * pagesize
    filters['limit'] = pagesize
  }

  return { queries, filters }
}

/**
 * 通用列表查询
 * @param model - 目标数据表的模型 
 * @param parmas - req.body中的查询参数对象
 * @param onlyOne - 是否只返回一条数据，false-数组形式返回所有数据，true-对象形式返回单条数据，默认为false
 * @returns 
 */
export const _list = async (model: Model<any>, params: IReqRestOptions, onlyOne: boolean = false, session: mongoose.ClientSession | null = null) => {
  const { queries, filters } = buildOptions(params)
  commLog.debug('queries=>', queries)
  commLog.debug('filters=>', filters)
  if (onlyOne)
    await model.findOne(queries).setOptions(filters).session(session).lean()
  else
    return await model.find(queries).setOptions(filters).session(session).lean()
}

/** 通用指定id单条查询 */
export const _oneForId = async (model: Model<any>, idOptions: IReqQueryByIdOptions, session: mongoose.ClientSession | null = null) => {
  const { _id, fill } = idOptions
  if (fill)
    return await model.findById(_id).populate(fill).session(session).lean()
  else
    return await model.findById(_id).session(session).lean()
}

/** 创建单条数据 */
export const _createOne = async <T>(model: mongoose.Model<any>, data: T, session: mongoose.ClientSession | null = null) => {
  const res = new model(data)
  await res.save({ session })
  return res
}

/** 创建多条数据 */
export const _createMany = async <T extends Object[]>(model: mongoose.Model<any>, data: T, session: mongoose.ClientSession | null = null) => {
  return await model.insertMany(data, { session })
}

/** 更新指定id的数据 */
export const _updateForId = async (model: Model<any>, _id: string, data: Object, session: mongoose.ClientSession | null = null) => {
  return await model.findByIdAndUpdate(_id, data, { session, new: true })
}

/** 删除指定id的数据 */
export const _removeForId = async (model: Model<any>, _id: string, session: mongoose.ClientSession | null = null) => {
  await model.findByIdAndDelete(_id).session(session)
  const count = await model.countDocuments({ _id }).session(session)
  return count === 0
}

/**
 * 事务处理
 * @param req - 请求对象
 * @param fun - 事务逻辑函数
 * @param param - 事务逻辑函数的参数
 * @returns - 返回一个对象，包含三个属性：data、msg和isValid。msg表示事务执行的结果，isValid表示事务是否成功，data表示事务执行的结果数据。
 */
export const _transaction = async (req: Request, fun: Function, param: Object) => {

  const session = await mongoose.startSession()

  req.dbSession = session
  req.isDBTransaction = true

  commLog.trace('事务开始')
  await session.startTransaction()

  // 这里执行逻辑
  let data = null
  if (fun)
    data = await fun(param, session)

  // throw new Error(ErrorCode.UNKNOW_DB_ERROR)

  // 提交事务
  await session.commitTransaction()
  req.dbSession = null
  req.isDBTransaction = false
  commLog.trace(`事务成功提交并结束`)

  return data

}